package org.ala.distributed_transaction_test.configuration;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;

/**
 * 数据库相关配置文件
 *
 * @author 骆毅(ala)
 * @date 2024年2月20日
 */
public class DBConfigure implements EnvironmentAware {
	
	
	/**
	 * 配置前缀
	 */
	static final String PRE_CONFIG = "datasource";
	
	
	/**
	 * 多数据源配置项
	 */
	protected Map<String, Item> itemHolder;
	
	
	@Override
	public void setEnvironment(Environment environment) {
		PropertyValues pvs = subProperties(environment, PRE_CONFIG);
		
		//	取配置的数据源名称
		Set<String> databaseNames = parseDatasetNames(pvs);
		itemHolder = databaseNames.stream().collect(Collectors.toMap(n->n, n->parseItem(pvs, n), (o,n)->n));
	}
	
	
	/**
	 * 获取子参数集
	 * @param env 环境上下文
	 * @param pre 参数前缀
	 * @return 子参数集
	 */
	@SuppressWarnings("rawtypes")
	public static PropertyValues subProperties(Environment env, String pre) {
		MutablePropertyValues mpvs = new MutablePropertyValues();
		Collection<String> ns = new HashSet<>();
		for (PropertySource<?> propertySource : ((AbstractEnvironment) env).getPropertySources()) {
			if (propertySource instanceof EnumerablePropertySource) {
				String[] propertyNames = ((EnumerablePropertySource) propertySource).getPropertyNames();
				for (String name : propertyNames) {
					if (name.startsWith(pre)) {
						if (!ns.add(name)) continue;
						mpvs.addPropertyValue(name, env.getProperty(name));
					}
				}
			}
		}
		return mpvs;
	}
	
	
	Pattern p = Pattern.compile(PRE_CONFIG + "\\.([\\w|\\d|_]+)\\.(.+)");
	/**
	 * 解析配置的数据源名字内容
	 */
	protected Set<String> parseDatasetNames(PropertyValues pvs) {
		Set<String> set = new HashSet<>();
		pvs.iterator().forEachRemaining(pv -> {
			Matcher matcher = p.matcher(pv.getName());
			if (matcher.matches()) {
				String name = matcher.group(1);
				set.add(name);
			}
		});
		return set;
	}
	
	
	/**
	 * 解析每个数据源配置
	 */
	protected Item parseItem(PropertyValues pvs, String databaseName) {
		Item item = new Item();
		item.setMapperLocations(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".mapper-locations").getValue().toString());
		item.setDriverClassName(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".driver-class-name").getValue().toString());
		item.setUrl(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".url").getValue().toString());
		item.setUsername(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".username").getValue().toString());
		item.setPassword(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".password").getValue().toString());
		//	解析druid配置
		DruidConfig druid = new DruidConfig();
		item.setDruid(druid);
		druid.setInitialSize(Integer.parseInt(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.initial-size").getValue().toString()));
		druid.setMinIdle(Integer.parseInt(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.min-idle").getValue().toString()));
		druid.setMaxActive(Integer.parseInt(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.max-active").getValue().toString()));
		druid.setMaxWait(Long.parseLong(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.max-wait").getValue().toString()));
		druid.setTimeBetweenEevictionRunsMillis(Long.parseLong(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.time-between-eviction-runs-millis").getValue().toString()));
		druid.setMinEvictableIdleTimeMillis(Long.parseLong(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.min-evictable-idle-time-millis").getValue().toString()));
		druid.setValidationQuery(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.validation-query").getValue().toString());
		druid.setTestWhileIdle(Boolean.parseBoolean(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.test-while-idle").getValue().toString()));
		druid.setTestOnBorrow(Boolean.parseBoolean(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.test-on-borrow").getValue().toString()));
		druid.setTestOnReturn(Boolean.parseBoolean(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.test-on-return").getValue().toString()));
		druid.setPoolPreparedStatements(Boolean.parseBoolean(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.pool-prepared-statements").getValue().toString()));
		druid.setMaxPoolPreparedStatementPerConnectionSize(Integer.parseInt(pvs.getPropertyValue(PRE_CONFIG + "." + databaseName + ".druid.max-pool-prepared-statement-per-connection-size").getValue().toString()));
		return item;
	}
	
	
	public Map<String, Item> getItemHolder() {
		return itemHolder;
	}
	public void setItemHolder(Map<String, Item> itemHolder) {
		this.itemHolder = itemHolder;
	}


	/**
	 * 配置项
	 */
	public static class Item {
		protected String mapperLocations;
		protected String driverClassName;
		protected String url;		
		protected String username;
		protected String password;
		protected DruidConfig druid;
		public String getMapperLocations() {
			return mapperLocations;
		}
		public void setMapperLocations(String mapperLocations) {
			this.mapperLocations = mapperLocations;
		}
		public String getDriverClassName() {
			return driverClassName;
		}
		public void setDriverClassName(String driverClassName) {
			this.driverClassName = driverClassName;
		}
		public String getUrl() {
			return url;
		}
		public void setUrl(String url) {
			this.url = url;
		}
		public String getUsername() {
			return username;
		}
		public void setUsername(String username) {
			this.username = username;
		}
		public String getPassword() {
			return password;
		}
		public void setPassword(String password) {
			this.password = password;
		}
		public DruidConfig getDruid() {
			return druid;
		}
		public void setDruid(DruidConfig druid) {
			this.druid = druid;
		}
	}
	/**
	 * druid连接池配置项
	 */
	public static class DruidConfig {
		protected int initialSize = 2;
	    protected int minIdle = 5;
	    protected int maxActive = 10;
	    protected long maxWait = 60000l;
	    protected long timeBetweenEevictionRunsMillis = 60000l;
	    protected long minEvictableIdleTimeMillis = 30000l;
	    protected String validationQuery = "select 1";
	    protected boolean testWhileIdle = true;
	    protected boolean testOnBorrow = true;
	    protected boolean testOnReturn = false;
	    //	是否缓存preparedStatement，也就是PSCache  官方建议MySQL下建议关闭   个人建议如果想用SQL防火墙 建议打开
	    protected boolean poolPreparedStatements = false;
	    protected int maxPoolPreparedStatementPerConnectionSize = 20;
		public int getInitialSize() {
			return initialSize;
		}
		public void setInitialSize(int initialSize) {
			this.initialSize = initialSize;
		}
		public int getMinIdle() {
			return minIdle;
		}
		public void setMinIdle(int minIdle) {
			this.minIdle = minIdle;
		}
		public int getMaxActive() {
			return maxActive;
		}
		public void setMaxActive(int maxActive) {
			this.maxActive = maxActive;
		}
		public long getMaxWait() {
			return maxWait;
		}
		public void setMaxWait(long maxWait) {
			this.maxWait = maxWait;
		}
		public long getTimeBetweenEevictionRunsMillis() {
			return timeBetweenEevictionRunsMillis;
		}
		public void setTimeBetweenEevictionRunsMillis(long timeBetweenEevictionRunsMillis) {
			this.timeBetweenEevictionRunsMillis = timeBetweenEevictionRunsMillis;
		}
		public long getMinEvictableIdleTimeMillis() {
			return minEvictableIdleTimeMillis;
		}
		public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
			this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
		}
		public String getValidationQuery() {
			return validationQuery;
		}
		public void setValidationQuery(String validationQuery) {
			this.validationQuery = validationQuery;
		}
		public boolean isTestWhileIdle() {
			return testWhileIdle;
		}
		public void setTestWhileIdle(boolean testWhileIdle) {
			this.testWhileIdle = testWhileIdle;
		}
		public boolean isTestOnBorrow() {
			return testOnBorrow;
		}
		public void setTestOnBorrow(boolean testOnBorrow) {
			this.testOnBorrow = testOnBorrow;
		}
		public boolean isTestOnReturn() {
			return testOnReturn;
		}
		public void setTestOnReturn(boolean testOnReturn) {
			this.testOnReturn = testOnReturn;
		}
		public boolean isPoolPreparedStatements() {
			return poolPreparedStatements;
		}
		public void setPoolPreparedStatements(boolean poolPreparedStatements) {
			this.poolPreparedStatements = poolPreparedStatements;
		}
		public int getMaxPoolPreparedStatementPerConnectionSize() {
			return maxPoolPreparedStatementPerConnectionSize;
		}
		public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
			this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
		}
	}
	
}
